#region Copyright Jonathan Quiros

//	
// All rights are reserved. Reproduction or transmission in whole or in 
// part, in any form or by any means, electronic, mechanical or otherwise, 
// is prohibited without the prior written consent of the copyright owner.
//
// Filename: Extensions.cs
//

#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Ids.Web.Listing;

namespace Novagoal.Repositories.Extensions
{
    public static class Extensions
    {
        #region CONSTANTS



        #endregion

        #region PROPERTIES

        

        #endregion

        #region PUBLIC METHODS

        #region Public Methods

        /// <summary>
        /// Sorts the element of a secuence in ascending or descending order according to a key.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
        /// <typeparam name="TKey">The type of the key returned by the function that is represented by <paramref name="keySelector"/>.</typeparam>
        /// <param name="source">A sequence of values to order.</param>
        /// <param name="keySelector">a function to extract a key from an element.</param>
        /// <param name="sortDirection">direction of the sort.</param>
        /// <exception cref="ArgumentNullException">source is null.</exception>
        /// <exception cref="ArgumentException">sortDirection is not recongized.</exception>
        /// <returns>An <see cref="System.Linq.IQueryable{T}" /> whose elements are sorted according to a key.</returns>
        public static IQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source,
            Expression<Func<TSource, TKey>> keySelector,
            SortDirectionType sortDirection)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            switch (sortDirection)
            {
                case SortDirectionType.Ascending:
                    return source.OrderBy(keySelector);

                case SortDirectionType.Descending:
                    return source.OrderByDescending(keySelector);

                default:
                    throw new ArgumentException("sortDirection provided is not reconigzed", "sortDirection");
            }
        }

        /// <summary>
        /// Method that takes a page from the source.
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static IQueryable<TSource> TakePage<TSource>(this IQueryable<TSource> source, int currentPage, int pageSize)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            var lastPage = Math.Ceiling(source.Count() / (double)pageSize);
            if (lastPage < currentPage && lastPage >= 1)
            {
                currentPage = (int)lastPage;
            }

            return source
                .Skip(pageSize * (currentPage - 1))
                .Take(pageSize);
        }

        /// <summary>
        /// Method that takes a page from the source  
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static IEnumerable<TSource> TakePage<TSource>(this IEnumerable<TSource> source, int currentPage, int pageSize)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            var lastPage = Math.Ceiling(source.Count() / (double)pageSize);
            if (lastPage < currentPage && lastPage >= 1)
            {
                currentPage = (int)lastPage;
            }

            return source
                .Skip(pageSize * (currentPage - 1))
                .Take(pageSize);
        }

        /// <summary>
        /// Creates a filtered and ordered "Page" of TResult from an IQueryable of TSource
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
        /// <typeparam name="TResult">The type of the value returned by the function represented by 
        /// <paramref name="selector"/>.</typeparam>
        /// <typeparam name="TKey">The type of the key returned by the function that is represented by 
        /// <paramref name="keySelector"/>.</typeparam>
        /// <param name="source">A sequence of values to project, filter, sort and paginate.</param>
        /// <param name="selector">a projection function to apply to each <paramref name="source"/> element.</param>
        /// <param name="predicates">functions to test each <paramref name="source"/> element for a condition.</param>
        /// <param name="keySelector">a function to extract a key from a <paramref name="source"/> element.</param>
        /// <param name="sortDirection">direction of the sort.</param>
        /// <param name="page">number of the "Page" to return. Skip the previous page - 1 Pages. It is one based.</param>
        /// <param name="count">Number of elements to return.</param>
        /// <exception cref="ArgumentNullException">source is null.</exception>
        /// <returns>A <see cref="System.Collections.Generic.List{T}"/> whose elements are the result of invoking the 
        /// projection function on each element of <paramref name="source"/>, that satisfy the conditions specified by the 
        /// <paramref name="predicates"/> and that is sorted according to a key.</returns>
        public static List<TResult> ToPage<TSource, TResult, TKey>(
            this IQueryable<TSource> source,
            Expression<Func<TSource, TResult>> selector,
            IEnumerable<Expression<Func<TResult, bool>>> predicates,
            Expression<Func<TResult, TKey>> keySelector,
            SortDirectionType sortDirection, int page, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            return predicates.Aggregate(source.Select(selector), (current, predicate) => current.Where(predicate))
                .OrderBy(keySelector, sortDirection)
                .TakePage(page, count)
                .ToList();
        }
        #endregion

        #endregion

        #region PRIVATE METHODS



        #endregion
    }
}